/*
 * WaMM.c ($RCSfile: WaMM.c,v $)
 *
 * The bulk of the code fro the reference implementation of the WaMM Hashing algorithm is in this file.
 *
 * Copyright 2008 John Washburn.  All rights reserved until I win the SHA3 contest.
 
$Header: D:\\RCSArchive\\D\\Programming\\WaMM\\ReferenceImplementation\\WaMM\\WaMM\\WaMM.c,v 1.7 2008-10-19 08:25:50-05 johnwashburn Exp johnwashburn $
$Revision: 1.7 $

$Log: WaMM.c,v $
Revision 1.7  2008-10-19 08:25:50-05  johnwashburn
Daily Checkpointing

Revision 1.6  2008-10-13 18:13:54-05  johnwashburn
Daily Check Point

Revision 1.2  2008-10-08 02:26:17-05  johnwashburn
Improved comment header block to better use RCS keywords

Revision 1.1  2008-10-08 02:08:03-05  johnwashburn
Added RCS Header Comment block using RCS Keywords.


*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "WaMM.h"
#include "WaMMOperator.h"
#include "WaMMTappingPrimes.h"
#include "WaMMErrorMessage.h"
#include "BitArray.h"
#include "ReverseBits.h"
#include "speed_test.h"

#define	MAX_MARKER_LEN		50
#define	SUBMITTER_INFO_LEN	128


#define ALGORITHM_NAME "wamm"

/* 
 * Set the storage location of the global variable for dumping intermediate states here and
 * set the default value to FALSE: i.e. No, don't dump intermediate states to stdout.
 */
BOOL g_bDumpIntermediateStates = FALSE;
FILE *g_fileDumpIntermediate = NULL;

HashReturn InitWaMMEnvironment()
{
	HashReturn retVal = FAIL;

	retVal = EnableBitArrays();
	if (SUCCESS == retVal)
	{
		retVal = InitErrorMessageBuffer();
		if (SUCCESS == retVal)
		{
			; /* NULL Statement.  Add the next Layer of initialization here. */
		}
	}

	return retVal;
}


BOOL IsHashLengthSupported(int hashbitlen)
{
	BOOL bIsSupported = FALSE;

/* Decide if the hash length is at least in range */
	if ((WaMM_HashLen_MaxBitLength < hashbitlen) ||
		(WaMM_HashLen_MinBitLength > hashbitlen))
	{
		sprintf(_WaMM_ErrorMesssage, "Hash bitlength of %d must be between %d and %d; inclusive.\n", hashbitlen, WaMM_HashLen_MinBitLength, WaMM_HashLen_MaxBitLength);
	}
	else
	{
		int iRemainder = hashbitlen % WaMM_HashLen_BitLengthModulus;
		if (0 < iRemainder)
		{
			sprintf(_WaMM_ErrorMesssage, "Hash bitlength of %d must be a multiple of %d.\n", hashbitlen, WaMM_HashLen_BitLengthModulus);
		}
		else
		{
			bIsSupported = TRUE;
		}
	}


	return bIsSupported;
}

HashReturn WaMM_DumpState(hashState *pState, char *szMessagePrefix)
{
	HashReturn retVal = FAIL;
	int iIndex, iRow, iCol;

	if (NULL != pState)
	{
		retVal = SUCCESS;

		if (NULL == g_fileDumpIntermediate)
		{
			g_fileDumpIntermediate = stdout;
		}

		if (NULL != szMessagePrefix)
		{
			fprintf(g_fileDumpIntermediate, "%s\n", szMessagePrefix);
		}

		fprintf(g_fileDumpIntermediate, "HashLength = %d bits.\n", pState->hashbitlen);
		fprintf(g_fileDumpIntermediate, "BitsProcessed = %lX bits.\n", pState->BitsProcessed);
		fprintf(g_fileDumpIntermediate, "Current Row of State Matrix = %d.\n", pState->iCurrRow);

		fprintf(g_fileDumpIntermediate, "[");
		for(iIndex = 0; iIndex < WaMM_VectorLength_Bytes; ++iIndex)
		{
			if ((0 == (iIndex % 4)) &&
				(0 != iIndex))
			{
				fprintf(g_fileDumpIntermediate, "| ");
			}
			fprintf(g_fileDumpIntermediate, "%2.2X ", pState->InputVector[iIndex]);
		}
		fprintf(g_fileDumpIntermediate, "] Input Vector.\n");

		fprintf(g_fileDumpIntermediate, "[");
		for(iIndex = 0; iIndex < WaMM_VectorLength_Bytes; ++iIndex)
		{
			if ((0 == (iIndex % 4)) &&
				(0 != iIndex))
			{
				fprintf(g_fileDumpIntermediate, "| ");
			}
			fprintf(g_fileDumpIntermediate, "%2.2X ", pState->MessageLength[iIndex]);
		}
		fprintf(g_fileDumpIntermediate, "] Message Length.\n");

		BitArrayToSring(pState->pUnprocessedBitArray, _WaMM_ErrorMesssage);
		fprintf(g_fileDumpIntermediate, "%s pUnprocessedBitArray.\n", _WaMM_ErrorMesssage);

		fprintf(g_fileDumpIntermediate, "\n");

		fprintf(g_fileDumpIntermediate, "[");
		for(iRow = 0; iRow < WaMM_MatrixDimension; ++iRow)
		{
			if (0 < iRow)
			{
				fprintf(g_fileDumpIntermediate, " ");
			}
			for(iCol = 0; iCol < WaMM_MatrixDimension; ++iCol)
			{
				if ((0 == (iCol % 4)) &&
					(0 != iCol))
				{
					fprintf(g_fileDumpIntermediate, "| ");
				}
				fprintf(g_fileDumpIntermediate, "%2.2X ", pState->stateMatrix[iRow][iCol]);
			}
			if (WaMM_MatrixDimension - 1 == iRow)
			{
				fprintf(g_fileDumpIntermediate, "] StateMatrix ");
			}
			fprintf(g_fileDumpIntermediate, "\n");
		}
	}
	else
	{
		retVal = ERORR_NULL_STATE_POINTER;
	}

	return retVal;
}

HashReturn InitWaMMVector(WaMM_DataVector pVector)
{
	HashReturn retVal = FAIL;

	if (NULL != pVector)
	{
		memset(pVector, 0, WaMM_VectorLength_BitSequences);
		retVal = SUCCESS;
	}
	else
	{
		retVal = ERORR_INITALIZE_NULL_VECTOR;
	}
	return retVal;
}

HashReturn CopyWaMMVector(WaMM_DataVector pDestinationVector, WaMM_DataVector pSourceVector)
{
	HashReturn retVal = FAIL;
	int i;

	if (NULL != pDestinationVector)
	{
		if (NULL != pDestinationVector)
		{
			for (i = 0; i < WaMM_VectorLength_Bytes; ++i)
			{
				pDestinationVector[i] = pSourceVector[i];
			}

			retVal = SUCCESS;
		}
		else
		{
			retVal = ERORR_COPY_NULL_VECTOR;
		}
	}
	else
	{
		retVal = ERORR_COPY_NULL_VECTOR;
	}

	return retVal;
}

HashReturn UpdateOneMemoryBlock(hashState *pState, const BitSequence *pData, long lBitsInBlock)
{
	HashReturn retVal = FAIL;
	BitArray *pBitArrayIncomingData = NULL;

	/* a negative bit length is an error */
	if (0 > lBitsInBlock)
	{
		retVal = ERORR_NEGATVELEN_BITARRAY;
	}
	else if (0 < lBitsInBlock)
	{
		if (NULL != pState)
		{
			if (IsHashLengthSupported(pState->hashbitlen))
			{
				if (NULL != pData)
				{
					// retVal = InitWaMMVector(pState->InputVector);
					retVal = InitWaMMVector(pState->InputVector);
					if (SUCCESS == retVal)
					{
						/* convert incoming bit sequence into a BitArray struct */
						pBitArrayIncomingData = BitArrayFromBitSequence(lBitsInBlock, pData);
						if (NULL != pBitArrayIncomingData)
						{
							/* Add the BitArray of the incoming bits to pUnprocessedBitArray */
							retVal = ConcatenateBitArray(pState->pUnprocessedBitArray, pBitArrayIncomingData);
							if (SUCCESS == retVal)
							{
								/* while full vectors of data exist within the incoming data
								 * transfer the full vectors to the and hash them
								 */
								while ((WaMM_VectorLength_Bits <= pState->pUnprocessedBitArray->lNumBits) &&
										(SUCCESS == retVal))
								{
									retVal = PopWholeVectorFromBitArray( pState->pUnprocessedBitArray, pState->InputVector);
									if (SUCCESS == retVal)
									{
										retVal = WaMMRoundFunction(pState);
										pState->BitsProcessed += WaMM_VectorLength_Bits;
									}
								}
							}
							FreeBitArray(pBitArrayIncomingData);
						}
						else
						{
							retVal = ERORR_MALLOC_BITARRAY;
						}
					}
				}
				else
				{
					retVal = ERORR_NULL_HASHDATA_POINTER;
				}
			}
			else
			{
				retVal = BAD_HASHLENGTH;
			}
		}
		else
		{
			retVal = ERORR_NULL_STATE_POINTER;
		}
	}
	else
	{
		/* Processing Zero Incoming bits is a NULL operation.  the state matrix is unchanged. */
		retVal = SUCCESS;
	}

	return retVal;
}

HashReturn AddToMessageSize(hashState *pState, DataLength databitlen)
{
	HashReturn retVal = FAIL;
	DataLength PrevSum;
	DataLength CurrSum;
	DataLength Carry = databitlen;
	DataLength *pNumber;
	int iIndex;
	int iNumNumbersInDataVector;

	if (NULL != pState)
	{
		if (0 > databitlen)
		{
			retVal = ERORR_NEGATVELEN_BITARRAY;
		}
		else if (0 < databitlen)
		{
			iNumNumbersInDataVector = WaMM_VectorLength_Bits / (sizeof(DataLength) * CHAR_BIT);
			for(iIndex = iNumNumbersInDataVector -1 ; (0 < Carry) && (0 <= iIndex); --iIndex)
			{
				/* Cast the DataVector in Message Length to an array of type DataLength
				 * The get the address of the number in the array of DataLength.
				 * If Little endian addressing is used, then you need to reverse the bytes to convert properly from
				 * little endian number of type DataLength into big-endian bit-sequences.
				 */
				pNumber = &(((DataLength *) pState->MessageLength)[iIndex]);
				PrevSum = *pNumber; 
				if (WaMM_LITTLE_ENDIAN)
				{
					PrevSum = ReverseBytesULongLong(PrevSum);
				}
				CurrSum = PrevSum + Carry;

				/* check for overflow in unsigned addition.  If found the a Carry to the next DataLength in the has occured. */
				if (CurrSum < PrevSum)
				{
					Carry = 1;
				}
				else
				{
					Carry = 0;
				}

				if (WaMM_LITTLE_ENDIAN)
				{
					CurrSum = ReverseBytesULongLong(CurrSum);
				}
				*pNumber = CurrSum;
			}

			retVal = SUCCESS;
		}
		else
		{
			/* adding zero is an easy success */
			retVal = SUCCESS;
		}
	}
	else
	{
		retVal = ERORR_NULL_STATE_POINTER;
	}
	
	return retVal;
}

HashReturn WaMMSquare(hashState *pState)
{
	HashReturn retVal = FAIL;
	int iRow, iCol, iIndex;
	int i, j;
	int iRound = 0;
	BYTE btProduct;
	WaMM_StateMatrix smSquared[WaMM_NumberOfRounds+1];

	if (NULL != pState)
	{
		retVal = SUCCESS;

#if 3 <= WAMM_DEBUGGING_LEVEL
		sprintf(_WaMM_ErrorMesssage, "\fBefore Squaring of SM");
		WaMM_DumpState(pState, _WaMM_ErrorMesssage);
#endif
		/* Transfer the current stateMatrix to the the 0 element of the mSquared array*/
		for( iRow = 0; iRow < WaMM_MatrixDimension; ++iRow)
		{
			for( iCol = 0; iCol < WaMM_MatrixDimension; ++iCol)
			{
				smSquared[0][iRow][iCol] = pState->stateMatrix[iRow][iCol];
			}
		}

		if (g_bDumpIntermediateStates)
		{
			sprintf(_WaMM_ErrorMesssage, "Squaring of SM after round 0");
			WaMM_DumpState(pState, _WaMM_ErrorMesssage);
		}

		/* Calculate the square of the matrix, smSquared[n-1], and store the results in smSquared[n]*/
		for( iRound = 1; iRound <= WaMM_NumberOfRounds; ++iRound)
		{
			for( iRow = 0; iRow < WaMM_MatrixDimension; ++iRow)
			{
				for( iCol = 0; iCol < WaMM_MatrixDimension; ++iCol)
				{
					smSquared[iRound][iRow][iCol] = 0;
					for( iIndex = 0; iIndex < WaMM_MatrixDimension; ++iIndex)
					{
						i = smSquared[iRound-1][ iRow ][iIndex];
						j = smSquared[iRound-1][iIndex][ iCol ];
						btProduct = gc_WaMMOperator[i][j];
						smSquared[iRound][iRow][iCol] += btProduct;
					}
				}
			}

			if (g_bDumpIntermediateStates)
			{
				for( iRow = 0; iRow < WaMM_MatrixDimension; ++iRow)
				{
					for( iCol = 0; iCol < WaMM_MatrixDimension; ++iCol)
					{
						pState->stateMatrix[iRow][iCol] = smSquared[iRound][iRow][iCol];
					}
				}
				sprintf(_WaMM_ErrorMesssage, "Squaring of SM after round %d", iRound);
				WaMM_DumpState(pState, _WaMM_ErrorMesssage);
			}
		}

		/* Transfer the last element of the smSquared array back to the stateMatrix */
		iRound = WaMM_NumberOfRounds;
		for( iRow = 0; iRow < WaMM_MatrixDimension; ++iRow)
		{
			for( iCol = 0; iCol < WaMM_MatrixDimension; ++iCol)
			{
				pState->stateMatrix[iRow][iCol] = smSquared[iRound][iRow][iCol];
			}
		}

#if 3 <= WAMM_DEBUGGING_LEVEL
		sprintf(_WaMM_ErrorMesssage, "\fSquaring of SM after round %d", iRound);
		WaMM_DumpState(pState, _WaMM_ErrorMesssage);
#endif

	}
	else
	{
		retVal = ERORR_NULL_STATE_POINTER;
	}

	return retVal;
}



HashReturn WaMMRoundFunction(hashState *pState)
{
	HashReturn retVal = FAIL;
	int iIndex;

	if (NULL != pState)
	{

		retVal = SUCCESS;

		if (g_bDumpIntermediateStates)
		{
			sprintf(_WaMM_ErrorMesssage, "\fInitial State of WaMMRoundFunction");
			WaMM_DumpState(pState, _WaMM_ErrorMesssage);
		}

		for(iIndex = 0; iIndex < WaMM_VectorLength_Bytes; ++iIndex)
		{
			pState->stateMatrix[pState->iCurrRow][iIndex] ^= pState->InputVector[iIndex];
		}

		++pState->iCurrRow;
		pState->iCurrRow %= WaMM_MatrixDimension;

		if (0 == pState->iCurrRow)
		{
			retVal = WaMMSquare(pState);
		}

		if (g_bDumpIntermediateStates &&
			(SUCCESS == retVal))
		{
			sprintf(_WaMM_ErrorMesssage, "\fInternal state after round Function");
			WaMM_DumpState(pState, _WaMM_ErrorMesssage);
		}
	}
	else
	{
		retVal = ERORR_NULL_STATE_POINTER;
	}

	return retVal;
}

////////////////////////////////////////////////////////////////////////////////////
typedef enum { KAT_SUCCESS = 0, KAT_FILE_OPEN_ERROR = 1, KAT_HEADER_ERROR = 2, KAT_DATA_ERROR = 3, KAT_HASH_ERROR = 4 } STATUS_CODES;

STATUS_CODES    genShortMsg(int hashbitlen);
STATUS_CODES    genLongMsg(int hashbitlen);
STATUS_CODES    genExtremelyLongMsg(int hashbitlen);
STATUS_CODES    genMonteCarlo(int hashbitlen);
int             FindMarker(FILE *infile, const char *marker);
int             ReadHex(FILE *infile, BitSequence *A, int Length, char *str);
void    fprintBstr(FILE *fp, char *S, BitSequence *A, int L);

STATUS_CODES
main()
{
        int             i, ret_val,  bitlens[4] = { 224, 256, 384, 512 };
        unsigned long timeStart = clock();
        for ( i=0; i<4; i++ ) {
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genShortMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genExtremelyLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genMonteCarlo(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
        }
        printf("overall test time: %lums", clock() - timeStart);
        return KAT_SUCCESS;
}

STATUS_CODES
genShortMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[256], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genShortMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ShortMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ShortMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else {
                        done = 1;
                        break;
                }
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <ShortMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished ShortMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[4288], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("LongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <LongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else
                        break;
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <LongMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished LongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genExtremelyLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Text[65], MD[64];
        int                     i, repeat;
        FILE            *fp_in;
        hashState       state;
        HashReturn      retval;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genExtremelyLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ExtremelyLongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ExtremelyLongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }


        if ( FindMarker(fp_in, "Repeat = ") )
                fscanf(fp_in, "%d", &repeat);
        else {
                printf("ERROR: unable to read 'Repeat' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

        if ( FindMarker(fp_in, "Text = ") )
                fscanf(fp_in, "%s", Text);
        else {
                printf("ERROR: unable to read 'Text' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

//      memcpy(Text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", 64);
        timeTest(0, logFile);   //test start
        timeTest(1, logFile);   //unit test start
        if ( (retval = Init(&state, hashbitlen)) != KAT_SUCCESS ) {
                printf("Init returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        for ( i=0; i<repeat; i++ )
        {
                if (i % 8192 == 0) timeTest(1, logFile);        //unit test start
                if ( (retval = Update(&state, Text, 512)) != KAT_SUCCESS ) {
                        printf("Update returned <%d> in genExtremelyLongMsg\n", retval);
                        return KAT_HASH_ERROR;
                }
                if (i % 8192 == 0) timeTest(2, logFile);        //unit test stop
        }
        timeTest(1, logFile);   //unit test start
        if ( (retval = Final(&state, MD)) != KAT_SUCCESS ) {
                printf("Final returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        timeTest(3, logFile);   //test stop
        printf("\tfinished ExtremelyLongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genMonteCarlo(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Seed[128], Msg[128], MD[64], Temp[128];
        int                     i, j, bytelen;
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genMonteCarlo_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("MonteCarlo.txt", "r")) == NULL ) {
                printf("Couldn't open <MonteCarlo.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        if ( !ReadHex(fp_in, Seed, 128, "Seed = ") ) {
                printf("ERROR: unable to read 'Seed' from <MonteCarlo.txt>\n");
                return KAT_DATA_ERROR;
        }

        bytelen = hashbitlen / 8;
        memcpy(Msg, Seed, 128);
        timeTest(0, logFile);   //test start
        for ( j=0; j<100; j++ ) {
                for ( i=0; i<1000; i++ ) {
                        timeTest(1, logFile);   //unit test start
                        Hash(hashbitlen, Msg, 1024, MD);
                        timeTest(2, logFile);   //unit test stop
                        memcpy(Temp, Msg, 128-bytelen);
                        memcpy(Msg, MD, bytelen);
                        memcpy(Msg+bytelen, Temp, 128-bytelen);
                }
        }
        timeTest(3, logFile);   //test stop
        printf("\tfinished MonteCarloKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
FindMarker(FILE *infile, const char *marker)
{
        char    line[MAX_MARKER_LEN];
        int             i, len;

        len = (int)strlen(marker);
        if ( len > MAX_MARKER_LEN-1 )
                len = MAX_MARKER_LEN-1;

        for ( i=0; i<len; i++ )
                if ( (line[i] = fgetc(infile)) == EOF )
                        return 0;
        line[len] = '\0';

        while ( 1 ) {
                if ( !strncmp(line, marker, len) )
                        return 1;

                for ( i=0; i<len-1; i++ )
                        line[i] = line[i+1];
                if ( (line[len-1] = fgetc(infile)) == EOF )
                        return 0;
                line[len] = '\0';
        }

        // shouldn't get here
        return 0;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
ReadHex(FILE *infile, BitSequence *A, int Length, char *str)
{
        int                     i, ch, started;
        BitSequence     ich;

        if ( Length == 0 ) {
                A[0] = 0x00;
                return 1;
        }
        memset(A, 0x00, Length);
        started = 0;
        if ( FindMarker(infile, str) )
                while ( (ch = fgetc(infile)) != EOF ) {
                        if ( !isxdigit(ch) ) {
                                if ( !started ) {
                                        if ( ch == '\n' )
                                                break;
                                        else
                                                continue;
                                }
                                else
                                        break;
                        }
                        started = 1;
                        if ( (ch >= '0') && (ch <= '9') )
                                ich = ch - '0';
                        else if ( (ch >= 'A') && (ch <= 'F') )
                                ich = ch - 'A' + 10;
                        else if ( (ch >= 'a') && (ch <= 'f') )
                                ich = ch - 'a' + 10;

                        for ( i=0; i<Length-1; i++ )
                                A[i] = (A[i] << 4) | (A[i+1] >> 4);
                        A[Length-1] = (A[Length-1] << 4) | ich;
                }
        else
                return 0;

        return 1;
}

void
fprintBstr(FILE *fp, char *S, BitSequence *A, int L)
{
        int             i;

        fprintf(fp, "%s", S);

        for ( i=0; i<L; i++ )
                fprintf(fp, "%02X", A[i]);

        if ( L == 0 )
                fprintf(fp, "00");

        fprintf(fp, "\n");
}
    
